package org.hackystat.devcathlon.engine.test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.datatype.XMLGregorianCalendar;

import org.hackystat.devcathlon.configuration.ConfigurationManager;
import org.hackystat.devcathlon.configuration.jaxb.DevcathlonConfiguration;
import org.hackystat.devcathlon.configuration.jaxb.EventConfiguration;
import org.hackystat.devcathlon.configuration.jaxb.Hackystat;
import org.hackystat.devcathlon.engine.event.Event;
import org.hackystat.devcathlon.engine.event.EventScoreManager;
import org.hackystat.devcathlon.engine.match.Match;
import org.hackystat.devcathlon.engine.match.MatchManager;
import org.hackystat.devcathlon.engine.team.Team;
import org.hackystat.devcathlon.engine.team.TeamManager;
import org.hackystat.devcathlon.engine.user.User;
import org.hackystat.devcathlon.engine.user.UserManager;
import org.hackystat.sensorbase.client.SensorBaseClient;
import org.hackystat.sensorbase.client.SensorBaseClient.InvitationReply;
import org.hackystat.sensorbase.resource.projects.jaxb.Project;
import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorData;
import org.hackystat.utilities.tstamp.Tstamp;
import org.junit.BeforeClass;

/**
 * Provides a superclass to be inherited by any Devcathlon test class that needs to run tests
 * against Hackystat services and/or set up Devcathlon Teams, Events, and Matches.
 * @author Philip Johnson
 *
 */
public class DevcathlonTestHelper {

  /** The Sensorbase server used in these tests. */
  private static org.hackystat.sensorbase.server.Server sensorbaseServer;
  /** The DailyProjectData server used in these tests. */
  private static org.hackystat.dailyprojectdata.server.Server dpdServer;
  /** The special Hackystat domain for test users. */
  private String domain = "@hackystat.org";
  
  private static final String users = "/Users/";
  private static final String foo = "/foo.java";
  private static final String ant = "Ant";

  /**
   * Constructor.
   * Increase the SensorBaseClient timeout globally to five seconds. 
   * By doing it this way, we don't have to call it every time we make a SensorBaseClient. 
   */
  public DevcathlonTestHelper () {
    System.setProperty(SensorBaseClient.SENSORBASECLIENT_TIMEOUT_KEY, "5000");
  }
  
  /** 
   * Starts the SensorBase and DPD services and initializes the DevcathlonConfiguration.
   * @throws Exception If problems occur setting up the services.  
   */
  @BeforeClass public static void setupServer() throws Exception {
    // Create testing versions of the Sensorbase, DPD, and Telemetry servers.
    DevcathlonTestHelper.sensorbaseServer = 
      org.hackystat.sensorbase.server.Server.newTestInstance();
    DevcathlonTestHelper.dpdServer = org.hackystat.dailyprojectdata.server.Server.newTestInstance();
    // Initialize all events.
    ConfigurationManager manager = ConfigurationManager.getInstance();
    DevcathlonConfiguration config = manager.getConfiguration();
    for (EventConfiguration eventConfig : config.getEventConfigurations().getEventConfiguration()) {
      manager.getConfiguration(eventConfig.getEvent());
    }
//    DevcathlonConfiguration config = new DevcathlonConfiguration();
    config.setHackystat(new Hackystat());
    config.getHackystat().setSensorbase(sensorbaseServer.getHostName());
    config.getHackystat().setDailyprojectdata(dpdServer.getHostName());
    // Use fake values for projectbrowser and telemetry for now since we don't use them for tests.
    config.getHackystat().setProjectbrowser("http://projectbrowser.com/");
    config.getHackystat().setTelemetry("http://telemetry.com/");
    ConfigurationManager.getInstance().setConfiguration(config);
    System.setProperty(SensorBaseClient.SENSORBASECLIENT_TIMEOUT_KEY, "10000");
  }
  

  
  /**
   * Returns the sensorbase hostname for these tests.
   * @return The host name, including the context root. 
   */
  protected String getSensorBaseHost() {
    return DevcathlonTestHelper.sensorbaseServer.getHostName();
  }
  
  /**
   * Returns the DPD hostname for these tests. 
   * @return The host name, including the context root. 
   */
  protected String getDailyProjectDataHost() {
    return DevcathlonTestHelper.dpdServer.getHostName();
  }
  
  /**
   * Creates and returns a new Devcathlon team for testing purposes.  This involves the following:
   * <ul>
   * <li> The owner and members (if any) are all registered with the Sensorbase. Their name will
   * be appended with the Sensorbase test suffix (@hackystat.org) to create their email address. 
   * <li> A Hackystat project called "name" is created. The project has a startDate of now and an
   * endDate of one year from now.  It has a UriPattern of *\{name}\*. 
   * <li> All members are invited to the project and they all accept the invitation.
   * <li> Devcathlon users are created for each of these members. 
   * <li> A Devcathlon team with the specified name is created with this owner and members. 
   * </ul>
   * @param keepIntegrity The boolean to keep or not to keep the db integrity.
   * @param name The name to be used for the Team and Hackystat Project and UriPattern. 
   * @param owner The owner of the Hackystat Project and the Devcathlon team. 
   * @param members The members of the Hackystat Project and the Devcathlon team. 
   * @return The newly created team. 
   * @throws Exception If any errors occur while creating the team. 
   */
  protected Team makeTeam(boolean keepIntegrity, String name, String owner, String ... members) 
  throws Exception {
    // [0] Delete any prior data that may exist from a prior unsuccessful test case. 
    deleteTeamData(keepIntegrity, name, owner, members);
    // the DB should take care of this.
    
    // [1] Register owner and users with the sensorbase. 
    String host = getSensorBaseHost();
    String ownerEmail = owner + domain;
    if (!(SensorBaseClient.isRegistered(host, ownerEmail, ownerEmail))) {
      SensorBaseClient.registerUser(host, ownerEmail);
    }
    for (String member : members) {
      if (!(SensorBaseClient.isRegistered(host, member + domain, member + domain))) {
        SensorBaseClient.registerUser(host, member + domain);
      }
    }
    // [2] Create project. 
    SensorBaseClient ownerClient = new SensorBaseClient(host, ownerEmail, ownerEmail);
    ownerClient.authenticate();
    Project project = new Project();
    project.setName(name);
    project.setOwner(ownerEmail);
    XMLGregorianCalendar startTime = Tstamp.makeTimestamp();
    project.setStartTime(Tstamp.incrementDays(startTime, -365));
    project.setEndTime(Tstamp.incrementDays(startTime, 365));
    project.addUriPattern("*/" + name + "/*");
    for (String member : members) {
      project.addInvitation(member + domain);
    }
    ownerClient.putProject(project);
    
    // [3] Have all members accept the invitation.
    for (String member : members) {
      String email = member + domain;
      SensorBaseClient client = new SensorBaseClient(host, email, email);
      client.authenticate();
      client.reply(ownerEmail, name, InvitationReply.ACCEPT);
    }
    
    // [4] Create Devcathlon owner and users.
    UserManager userManager = UserManager.getInstance();
    User teamOwner = userManager.addUser("Joe", owner, ownerEmail, ownerEmail);
    Set<User> teamMembers = new HashSet<User>();
    for (String member : members) {
      User teamMember = userManager.addUser("Bob", member, member + domain, member + domain);
      teamMembers.add(teamMember);
    }
    // Create the team.
    Team team = TeamManager.getInstance().addTeam(name, teamOwner, teamMembers);
    // Associate the team with the project.
    team.setProject(project);
    team = TeamManager.getInstance().updateTeam(team);
    
    return team;
  }
  
  /**
   * Creates and returns a new Devcathlon team for testing purposes.  This involves the following:
   * <ul>
   * <li> The owner and members (if any) are all registered with the Sensorbase. Their name will
   * be appended with the Sensorbase test suffix (@hackystat.org) to create their email address. 
   * <li> A Hackystat project called "name" is created. The project has a startDate of now and an
   * endDate of one year from now.  It has a UriPattern of *\{name}\*. 
   * <li> All members are invited to the project and they all accept the invitation.
   * <li> Devcathlon users are created for each of these members. 
   * <li> A Devcathlon team with the specified name is created with this owner and members. 
   * </ul>
   * @param name The name to be used for the Team and Hackystat Project and UriPattern. 
   * @param owner The owner of the Hackystat Project and the Devcathlon team. 
   * @param members The members of the Hackystat Project and the Devcathlon team. 
   * @return The newly created team. 
   * @throws Exception If any errors occur while creating the team. 
   */
  protected Team makeTeam(String name, String owner, String ... members) throws Exception {
    // [0] Delete any prior data that may exist from a prior unsuccessful test case. 
    deleteTeamData(name, owner, members);
    // the DB should take care of this.
    
    // [1] Register owner and users with the sensorbase. 
    String host = getSensorBaseHost();
    String ownerEmail = owner + domain;
    if (!(SensorBaseClient.isRegistered(host, ownerEmail, ownerEmail))) {
      SensorBaseClient.registerUser(host, ownerEmail);
    }
    for (String member : members) {
      if (!(SensorBaseClient.isRegistered(host, member + domain, member + domain))) {
        SensorBaseClient.registerUser(host, member + domain);
      }
    }
    // [2] Create project. 
    SensorBaseClient ownerClient = new SensorBaseClient(host, ownerEmail, ownerEmail);
    ownerClient.authenticate();
    Project project = new Project();
    project.setName(name);
    project.setOwner(ownerEmail);
    XMLGregorianCalendar startTime = Tstamp.makeTimestamp();
    project.setStartTime(Tstamp.incrementDays(startTime, -365));
    project.setEndTime(Tstamp.incrementDays(startTime, 365));
    project.addUriPattern("*/" + name + "/*");
    for (String member : members) {
      project.addInvitation(member + domain);
    }
    ownerClient.putProject(project);
    
    // [3] Have all members accept the invitation.
    for (String member : members) {
      String email = member + domain;
      SensorBaseClient client = new SensorBaseClient(host, email, email);
      client.authenticate();
      client.reply(ownerEmail, name, InvitationReply.ACCEPT);
    }
    
    // [4] Create Devcathlon owner and users.
    UserManager userManager = UserManager.getInstance();
    User teamOwner = userManager.addUser("Joe", owner, ownerEmail, ownerEmail);
    Set<User> teamMembers = new HashSet<User>();
    for (String member : members) {
      User teamMember = userManager.addUser("Bob", member, member + domain, member + domain);
      teamMembers.add(teamMember);
    }
    // Create the team.
    Team team = TeamManager.getInstance().addTeam(name, teamOwner, teamMembers);
    // Associate the team with the project.
    team.setProject(project);
    team = TeamManager.getInstance().updateTeam(team);
    
    return team;
  }
  
  /**
   * Same as makeTeam function but sends out invites as well.
   * 
   * @param name The name to be used for the Team and Hackystat Project and UriPattern. 
   * @param owner The owner of the Hackystat Project and the Devcathlon team. 
   * @param members The members of the Hackystat Project and the Devcathlon team. 
   * @return The newly created team. 
   * @throws Exception If any errors occur while creating the team. 
   */
  protected Team makeTeamAndInvites
    (String name, String owner, String ... members) throws Exception {
    // [0] Delete any prior data that may exist from a prior unsuccessful test case. 
    deleteTeamData(name, owner, members);
    // [1] Register owner and users with the sensorbase. 
    String host = getSensorBaseHost();
    String ownerEmail = owner + domain;
    if (!(SensorBaseClient.isRegistered(host, ownerEmail, ownerEmail))) {
      SensorBaseClient.registerUser(host, ownerEmail);
    }
    for (String member : members) {
      if (!(SensorBaseClient.isRegistered(host, member + domain, member + domain))) {
        SensorBaseClient.registerUser(host, member + domain);
      }
    }
    // [2] Create project. 
    SensorBaseClient ownerClient = new SensorBaseClient(host, ownerEmail, ownerEmail);
    ownerClient.authenticate();
    Project project = new Project();
    project.setName(name);
    project.setOwner(ownerEmail);
    XMLGregorianCalendar startTime = Tstamp.makeTimestamp();
    project.setStartTime(Tstamp.incrementDays(startTime, -365));
    project.setEndTime(Tstamp.incrementDays(startTime, 365));
    project.addUriPattern("*/" + name + "/*");
    for (String member : members) {
      project.addInvitation(member + domain);
    }    
    ownerClient.putProject(project);
    
    // [3] Have all members accept the invitation.
    for (String member : members) {
      String email = member + domain;
      SensorBaseClient client = new SensorBaseClient(host, email, email);
      client.authenticate();
      client.reply(ownerEmail, name, InvitationReply.ACCEPT);
    }
    
    // [4] Create Devcathlon owner and users.
    UserManager userManager = UserManager.getInstance();
    User teamOwner = userManager.addUser("Joe", owner, ownerEmail, ownerEmail);
    Set<User> teamMembers = new HashSet<User>();
    for (String member : members) {
      User teamMember = userManager.addUser("Bob", member, member + domain, member + domain);
      teamMembers.add(teamMember);
    }
    
    // Make the team and return it. 
    Set<User> ownerAsMember = new HashSet<User>();
    ownerAsMember.add(teamOwner);
    Team team = TeamManager.getInstance().addTeam(name, teamOwner, ownerAsMember);
    team.sendInvites(teamMembers);
    team.setProject(project);
    // Update the team instance.
    team = TeamManager.getInstance().updateTeam(team);
    return team;
  }
  
  /**
   * Deletes the specified team, its owner, and its members from Devcathlon and from the test
   * Hackystat sensorbase. 
   * @param name The name of the team to be deleted. 
   * @param keepIntegrity The boolean to keep or not to keep the integrity.
   * @throws Exception If problems occur. 
   */
  protected void deleteTeam(String name, boolean keepIntegrity) throws Exception {
    String host = getSensorBaseHost();

    User teamOwner = TeamManager.getInstance().getTeam(name).getOwner();
    String ownerEmail = teamOwner.getEmail();
    Set<User> teamMembers = TeamManager.getInstance().getTeam(name).getMembers();
    // [1] Delete the Hackystat project. 
    SensorBaseClient ownerClient = new SensorBaseClient(host, ownerEmail, ownerEmail);
    ownerClient.deleteProject(ownerEmail, name);
    // [2] Delete the Hackystat users.
    deleteHackystatUser(ownerEmail);
    for (User teamMember : teamMembers) {
      deleteHackystatUser(teamMember.getEmail());
    }
    // [*] Destroy all EventScore(s) if related to this give team
    EventScoreManager.getInstance().deleteEventScore(name);
    // [3] Delete the Devcathlon team. 
    TeamManager.getInstance().deleteTeam(name);
    // [4] Delete the Devcathlon users.
    if (!keepIntegrity) {
      UserManager.getInstance().deleteUser(teamOwner.getEmail());
      for (User teamMember : teamMembers) {
        UserManager.getInstance().deleteUser(teamMember.getEmail());
      }
    }
  }
  
  /**
   * Deletes the specified team, its owner, and its members from Devcathlon and from the test
   * Hackystat sensorbase. 
   * @param name The name of the team to be deleted. 
   * @throws Exception If problems occur. 
   */
  protected void deleteTeam(String name) throws Exception {
    String host = getSensorBaseHost();

    User teamOwner = TeamManager.getInstance().getTeam(name).getOwner();
    String ownerEmail = teamOwner.getEmail();
    Set<User> teamMembers = TeamManager.getInstance().getTeam(name).getMembers();
    // [1] Delete the Hackystat project. 
    SensorBaseClient ownerClient = new SensorBaseClient(host, ownerEmail, ownerEmail);
    ownerClient.deleteProject(ownerEmail, name);
    // [2] Delete the Hackystat users.
    deleteHackystatUser(ownerEmail);
    for (User teamMember : teamMembers) {
      deleteHackystatUser(teamMember.getEmail());
    }
    // [*] Destroy all EventScore(s) if related to this give team
    EventScoreManager.getInstance().deleteEventScore(name);
    // [3] Delete the Devcathlon team. 
    TeamManager.getInstance().deleteTeam(name);
    // [4] Delete the Devcathlon users.
    UserManager.getInstance().deleteUser(teamOwner.getEmail());
    for (User teamMember : teamMembers) {
      UserManager.getInstance().deleteUser(teamMember.getEmail());
    }
  }
  
  /**
   * Ensures that the specified team name, owner, and members are not present in the system
   * or in the underlying Hackystat database.  This method is used by the makeTeam method 
   * to ensure that all underlying data structures are "clean" before creating the new team.
   * @param keepIntegrity The boolean to keep or not to keep db integrity. 
   * @param name The team name.
   * @param owner The team owner. 
   * @param members The team members.
   * @throws Exception if problems occur during cleanup. 
   */
  private void deleteTeamData(boolean keepIntegrity, String name, String owner, String ... members) 
  throws Exception {
    String host = getSensorBaseHost();
    String ownerEmail = owner + domain;
    // [1] Delete the Hackystat project, if it exists.
    SensorBaseClient ownerClient = new SensorBaseClient(host, ownerEmail, ownerEmail);
    if (ownerClient.inProject(ownerEmail, name)) {
      ownerClient.deleteProject(ownerEmail, name);
    }
    // [2] Delete the Hackystat users, if they exist. 
    deleteHackystatUser(ownerEmail);
    for (String member : members) {
      deleteHackystatUser(member + domain);
    }
    // [3] Delete the Devcathlon team, if it exists. 
    TeamManager.getInstance().deleteTeam(name);
    // [4] Delete the Devcathlon users, if they exist. 
    if (!(keepIntegrity)) {
      UserManager.getInstance().deleteUser(ownerEmail);
      for (String member : members) {
        UserManager.getInstance().deleteUser(member + domain);
      }
    }
  }
  
  /**
   * Ensures that the specified team name, owner, and members are not present in the system
   * or in the underlying Hackystat database.  This method is used by the makeTeam method 
   * to ensure that all underlying data structures are "clean" before creating the new team. 
   * @param name The team name.
   * @param owner The team owner. 
   * @param members The team members.
   * @throws Exception if problems occur during cleanup. 
   */
  private void deleteTeamData(String name, String owner, String ... members) throws Exception {
    String host = getSensorBaseHost();
    String ownerEmail = owner + domain;
    // [1] Delete the Hackystat project, if it exists.
    SensorBaseClient ownerClient = new SensorBaseClient(host, ownerEmail, ownerEmail);
    if (ownerClient.inProject(ownerEmail, name)) {
      ownerClient.deleteProject(ownerEmail, name);
    }
    // [2] Delete the Hackystat users, if they exist. 
    deleteHackystatUser(ownerEmail);
    for (String member : members) {
      deleteHackystatUser(member + domain);
    }
    // [3] Delete the Devcathlon team, if it exists. 
    TeamManager.getInstance().deleteTeam(name);
    // [4] Delete the Devcathlon users, if they exist. 
    UserManager.getInstance().deleteUser(ownerEmail);
    for (String member : members) {
      UserManager.getInstance().deleteUser(member + domain);
    }
  }
  
  /**
   * Returns the Project instance for this owner (email) and project name.
   * @param owner The owner.
   * @param name The project name.
   * @return The Project instance. 
   * @throws Exception If the project cannot be retrieved from the sensorbase. 
   */
  protected Project getProject(String owner, String name) throws Exception {
    String email = owner + domain;
    SensorBaseClient client = new SensorBaseClient(getSensorBaseHost(), email, email);
    return client.getProject(email, name);
  }

  /**
   * Deletes the data associated with this user from the SensorBase, followed by the user itself.
   * If the user is already deleted from the SensorBase, does nothing. 
   * @param email The user to be deleted. 
   * @throws Exception If problems occur. 
   */
  private void deleteHackystatUser(String email) throws Exception {
    String host = getSensorBaseHost();
    if (SensorBaseClient.isRegistered(host, email, email)) {
      SensorBaseClient client = new SensorBaseClient(host, email, email);
      client.deleteSensorData(email);
      client.deleteUser(email);
    }
  }
  
  /**
   * Defines a "simple" match containing a single team, project, and event. 
   * @param name The name of this match. 
   * @param team The single team in this match.
   * @param project The project associated with this team. 
   * @param event The single event in this match.
   * @return The match that was created. 
   * @throws Exception If problems occur, such as a duplicate match name. 
   */
  protected Match addSimpleMatch(String name, Team team, Project project, Event event) throws
  Exception {
    // Delete this match if it already exists.
    MatchManager.getInstance().deleteMatch(name);
    // Now create the new one.  
    XMLGregorianCalendar startTime = Tstamp.makeTimestamp();
    Map<Team, Project> team2project = new HashMap<Team, Project>();
    team2project.put(team, project);
    List<Event> events = new ArrayList<Event>();
    events.add(event);
    return MatchManager.getInstance().addMatch(name, Tstamp.incrementDays(startTime, -364), 
        Tstamp.incrementDays(startTime, 364), team2project, events);
  }
  
  /**
   * Defines a match containing two teams, the two projects, and event. 
   * @param name The name of this match. 
   * @param team1 The first team in this match.
   * @param team2 The second team in this match.
   * @param project1 The project that will be associated with team1.
   * @param project2 The project that will be associated with team2. 
   * @param event The single event in this match.
   * @return The match that was created. 
   * @throws Exception If problems occur, such as a duplicate match name. 
   */
  protected Match addTwoTeamMatch(String name, Team team1, Team team2, 
      Project project1, Project project2, Event event) throws Exception {
    // Delete this match if it already exists.
    MatchManager.getInstance().deleteMatch(name);
    // Now create the new one.  
    XMLGregorianCalendar startTime = Tstamp.makeTimestamp();
    Map<Team, Project> team2project = new HashMap<Team, Project>();
    team2project.put(team1, project1);
    team2project.put(team2, project2);
    List<Event> events = new ArrayList<Event>();
    events.add(event);
    return MatchManager.getInstance().addMatch(name, Tstamp.incrementDays(startTime, -364),
        Tstamp.incrementDays(startTime, 364), team2project, events);
  }

  /**
   * Defines a "simple" match containing a single team, project, and event 
   * where the match length is 5 days.  
   * @param name The name of this match. 
   * @param team The single team in this match.
   * @param project The project associated with this team. 
   * @param event The single event in this match.
   * @return The match that was created. 
   * @throws Exception If problems occur, such as a duplicate match name. 
   */
  protected Match addSimpleShortMatch(String name, Team team, Project project, Event event) throws
  Exception {
    // Delete this match if it already exists.
    MatchManager.getInstance().deleteMatch(name);
    // Now create the new one.  
    XMLGregorianCalendar startTime = Tstamp.makeTimestamp();
    Map<Team, Project> team2project = new HashMap<Team, Project>();
    team2project.put(team, project);
    List<Event> events = new ArrayList<Event>();
    events.add(event);
    return MatchManager.getInstance().addMatch(name, startTime, 
        Tstamp.incrementDays(startTime, 5), team2project, events);
  }  
  
  /**
   * Deletes the specified match.
   * @param name The name of the match.
   */
  protected void deleteMatch(String name) {
    MatchManager.getInstance().deleteMatch(name);
  }
  
  /**
   * Sends a Build sensor data instance for the given owner, project, and time.
   * The build data has a (Type, continuous.integration) property.
   * It is a passing build.
   * @param owner The owner of the data to be sent.
   * @param project The project, used to generate the Resource string.
   * @param timestamp The timestamp for this sensor data.
   * @throws Exception If problems occur.
   */
  protected void makeBuildData(User owner, Project project, XMLGregorianCalendar timestamp) 
  throws Exception {
    String host = getSensorBaseHost();
    String email = owner.getEmail();
    SensorBaseClient client = new SensorBaseClient(host, email, email);
    SensorData data = new SensorData();
    data.setOwner(email);
    data.setTool(ant);
    data.setSensorDataType("Build");
    data.setResource(users + project.getName() + foo);
    data.addProperty("Type", "continuous.integration");
    data.addProperty("Result", "Success");
    data.setTimestamp(timestamp);
    client.putSensorData(data);
  }
  
  /**
   * Sends a Build sensor data instance for the given owner, project, and time.
   * The build data has a (Type, continuous.integration) property.
   * It is a passing build.
   * @param owner The owner of the data to be sent.
   * @param project The project, used to generate the Resource string.
   * @param timestamp The timestamp for this sensor data.
   * @param success True if it is supposed to be a passing build.
   * @throws Exception If problems occur.
   */
  protected void makeBuildData(User owner, Project project, XMLGregorianCalendar timestamp,
      boolean success) throws Exception {
    String host = getSensorBaseHost();
    String email = owner.getEmail();
    SensorBaseClient client = new SensorBaseClient(host, email, email);
    SensorData data = new SensorData();
    data.setOwner(email);
    data.setTool(ant);
    data.setSensorDataType("Build");
    data.setResource(users + project.getName() + foo);
    data.addProperty("Type", "continuous.integration");
    data.addProperty("Result", (success ? "Success" : "Failure"));
    data.setTimestamp(timestamp);
    client.putSensorData(data);
  }
  
  /**
   * Sends a Commit sensor data instance for the given owner, project, and time.
   * The commit data has a (linesDeleted and linesAdded) property.
   * @param owner The owner of the data to be sent.
   * @param project The project, used to generate the Resource string.
   * @param timestamp The timestamp for this sensor data.
   * @throws Exception If problems occur.
   */
  protected void makeCommitData(User owner, Project project, XMLGregorianCalendar timestamp) 
  throws Exception {
    String host = getSensorBaseHost();
    String email = owner.getEmail();
    SensorBaseClient client = new SensorBaseClient(host, email, email);
    SensorData data = new SensorData();
    data.setOwner(email);
    data.setTool(ant);
    data.setSensorDataType("Commit");
    data.setResource(users + project.getName() + foo);
    data.setTimestamp(timestamp);
    data.addProperty("linesAdded", "10");
    data.addProperty("linesDeleted", "10");
    client.putSensorData(data);
  }
  
  /**
   * Sends a Commit sensor data instance for the given owner, project, time, lines added, and 
   * lines deleted.  The commit data has a (linesDeleted and linesAdded) property.
   * @param owner The owner of the data to be sent.
   * @param project The project, used to generate the Resource string.
   * @param timestamp The timestamp for this sensor data.
   * @param linesAdded The amount of lines added.
   * @param linesDeleted The amount of lines deleted.
   * @throws Exception If problems occur.
   */
  protected void makeCommitData(User owner, Project project, XMLGregorianCalendar timestamp,
      String linesAdded, String linesDeleted) throws Exception {
    String host = getSensorBaseHost();
    String email = owner.getEmail();
    SensorBaseClient client = new SensorBaseClient(host, email, email);
    SensorData data = new SensorData();
    data.setOwner(email);
    data.setTool(ant);
    data.setSensorDataType("Commit");
    data.setResource(users + project.getName() + foo);
    data.setTimestamp(timestamp);
    data.addProperty("linesAdded", linesAdded);
    data.addProperty("linesDeleted", linesDeleted);
    client.putSensorData(data);
  }
  
  /**
   * Sends a single Coverage sensor data instance representing the specified Line-level coverage.
   * @param owner The owner of the data to be sent.
   * @param project The project, used to generate the Resource string.
   * @param timestamp The timestamp (and runtime) for the Sensor Data instance. 
   * @param percent The target percentage line-level coverage.
   * @param loc The total lines of code. 
   * @throws Exception If problems occur or if passed percentage is not between 0 and 100.
   */
  protected void makeCoverageData(User owner, Project project, XMLGregorianCalendar timestamp,
      int percent, int loc) throws Exception {
    if ((percent < 0) || (percent > 100)) {
      throw new Exception("Percent must be between 0 and 100");
    }
    String host = getSensorBaseHost();
    String email = owner.getEmail();
    SensorBaseClient client = new SensorBaseClient(host, email, email);
    SensorData data = new SensorData();
    data.setOwner(email);
    data.setTool("Emma");
    data.setSensorDataType("Coverage");
    data.setResource(users + project.getName() + foo);
    data.setTimestamp(timestamp);
    data.setRuntime(timestamp);
    int covered = (int)(loc * (percent / 100.0));
    int uncovered = loc - covered;
    data.addProperty("line_Covered", String.valueOf(covered));
    data.addProperty("line_Uncovered", String.valueOf(uncovered));
    client.putSensorData(data);
  }
  /**
   * Creates the specified amount of DevTime by generating one DevEvent every five minutes from
   * the startTime until totalDevTime has been reached.  So, if the desired amount of DevTime in
   * this project is 30 minutes, then 6 DevEvent instances will be generated. 
   * @param owner The owner of this DevEvent data. 
   * @param project The project this data will be associated with.
   * @param startTime The start time. 
   * @param totalDevTime The total amount of DevTime that will be created by this method.
   * @throws Exception If problems occur generating the data. 
   */
  protected void makeDevTimeData (User owner, Project project, XMLGregorianCalendar startTime,
      int totalDevTime) throws Exception {
    makeDevTimeData (owner, project, startTime, totalDevTime, foo);
  }
  /**
   * Creates the specified amount of DevTime by generating one DevEvent every five minutes from
   * the startTime until totalDevTime has been reached.  So, if the desired amount of DevTime in
   * this project is 30 minutes, then 6 DevEvent instances will be generated. 
   * @param owner The owner of this DevEvent data. 
   * @param project The project this data will be associated with.
   * @param startTime The start time. 
   * @param totalDevTime The total amount of DevTime that will be created by this method.
   * @param filename A file. 
   * @throws Exception If problems occur generating the data. 
   */
  protected void makeDevTimeData (User owner, Project project, XMLGregorianCalendar startTime,
      int totalDevTime, String filename) throws Exception {
   if (totalDevTime % 5 != 0) {
     throw new Exception("totalDevTime must be a multiple of 5.");
   }
   String host = getSensorBaseHost();
   String email = owner.getEmail();
   SensorBaseClient client = new SensorBaseClient(host, email, email);
   
   for (int i = 0; i < totalDevTime; i += 5) {
     XMLGregorianCalendar timestamp = Tstamp.incrementMinutes(startTime, i);
     SensorData data = new SensorData();
     data.setOwner(email);
     data.setTool("Eclipse");
     data.setSensorDataType("DevEvent");
     data.setResource(users + project.getName() + filename);
     data.setTimestamp(timestamp);
     client.putSensorData(data);
   }
   
  }
  
  /**
   * Initializes all events defined in the configuration.xml file.
   * @throws Exception If problems occur.
   */
  protected synchronized void initAllEvents() throws Exception {
    ConfigurationManager manager = ConfigurationManager.getInstance();
    DevcathlonConfiguration config = manager.getConfiguration();
    for (EventConfiguration eventConfig : config.getEventConfigurations().getEventConfiguration()) {
      manager.getConfiguration(eventConfig.getEvent());
    }
  }
}
